﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Reactive.Linq;
using System.Threading;
using Caliburn.Micro;
using PurchaseSales.Manage.Common;
using PurchaseSales.Manage.Common.AOP;
using PurchaseSales.Manage.Common.Extensions;
using PurchaseSales.Manage.Model.Entities;
using PurchaseSales.Manage.Services;
using PurchaseSales.Manage.Views.UserControls;
using ReactiveUI;
using ReactiveUI.Xaml;

namespace PurchaseSales.Manage.ViewModels
{
    public class AuthRoleViewModel : Screen
    {
        private readonly IAuthRolesRepository _repository;
        private readonly IAuthRoleFunctionRepository _roleFunctions;
        private readonly IUserRepository _userRepository;
        private readonly IWindowManager _windowManager;
        private Dictionary<string, string> _allFunction;

        public ReactiveAsyncCommand SetRoleCommand { get; set; }
        public ReactiveAsyncCommand SelectedRoleCommand { get; set; }
        public ReactiveAsyncCommand SelectedUserCommand { get; set; }
        public ReactiveAsyncCommand CheckedCommand { get; set; }
        public ReactiveAsyncCommand UncheckedCommand { get; set; }
        public ReactiveAsyncCommand AddCommand { get; set; }
        public ReactiveAsyncCommand RemoveCommand { get; set; }
        public ReactiveAsyncCommand EditCommand { get; set; }
        public ReactiveAsyncCommand AddUserCommand { get; set; }
        public ReactiveAsyncCommand EditUserCommand { get; set; }
        public ReactiveAsyncCommand RemoveUserCommand { get; set; }

        public ReactiveAsyncCommand LoadUserCommand { get; set; }
        public ReactiveAsyncCommand LoadAuthRoleCommand { get; set; }

        public ObservableCollection<AuthModule> AuthModules { get; set; }
        private ObservableAsPropertyHelper<AuthRoles> _authRoles;
        private ObservableAsPropertyHelper<Users> _user;
        private ObservableAsPropertyHelper<AuthRoles> _baseAuthRole;
        private ObservableAsPropertyHelper<ReactiveCollection<Users>> _loadUserCollection;
        private ObservableAsPropertyHelper<ReactiveCollection<AuthRoles>> _loadAuthRoleCollection;

        public AuthRoleViewModel(IAuthRolesRepository repository, IAuthRoleFunctionRepository roleFunctions, IUserRepository userRepository, IWindowManager windowManager)
        {
            _repository = repository;
            _roleFunctions = roleFunctions;
            _userRepository = userRepository;
            _windowManager = windowManager;

            LoadUserCommand = new ReactiveAsyncCommand();
            var loadUserObservable = LoadUserCommand.RegisterAsyncFunction(o =>
                                    {
                                        var users = _userRepository.GetAll();
                                        return users.ToObservable().CreateCollection();
                                    });
            LoadUserCommand.Execute(this);

            _loadUserCollection = new ObservableAsPropertyHelper<ReactiveCollection<Users>>(loadUserObservable, _ => NotifyOfPropertyChange(() => UsersCollection));

            LoadAuthRoleCommand = new ReactiveAsyncCommand();
            var loadAuthRoleObservable = LoadAuthRoleCommand.RegisterAsyncFunction(o =>
            {
                Thread.Sleep(1000);
                var authRoleses = _repository.GetAllAuthRole();
                return authRoleses.ToObservable().CreateCollection();
            });
            _loadAuthRoleCollection = new ObservableAsPropertyHelper<ReactiveCollection<AuthRoles>>(loadAuthRoleObservable, _ => { NotifyOfPropertyChange(() => AuthRolesCollection); });
            LoadAuthRoleCommand.Execute(this);


            SetRoleCommand = new ReactiveAsyncCommand();
            var authrole = SetRoleCommand.RegisterAsyncFunction(o =>
                                        {
                                            var authRoles = o as AuthRoles;
                                            return authRoles;
                                        });
            var baseauthrole = authrole.Select(x => x != null).StartWith(false);
            _baseAuthRole = new ObservableAsPropertyHelper<AuthRoles>(authrole, _ => { });

            SelectedRoleCommand = new ReactiveAsyncCommand();
            var authObservable = SelectedRoleCommand.Select(o =>
                                {
                                    var authRoles = o as AuthRoles;
                                    if (authRoles == null)
                                        return null;
                                    AuthModules.Clear();

                                    var functions = _roleFunctions.GetRoleFunctionByRoleId(authRoles);
                                    IsRoleSelected = true;
                                    foreach (var i in _allFunction)
                                    {
                                        if (functions != null)
                                        {
                                            var hasAuth = functions.Any(x => x.FunctionKey == i.Key);
                                            var isselected = false;
                                            if (hasAuth)
                                                isselected = true;
                                            AuthModules.Add(new AuthModule(i.Key, i.Value, isselected));
                                        }
                                    }
                                    return authRoles;
                                }).SubscribeOnDispatcher();
            _authRoles = new ObservableAsPropertyHelper<AuthRoles>(authObservable, _ => { });

            var roleSelected = authObservable.Select(x => x != null).StartWith(false);
            CheckedCommand = new ReactiveAsyncCommand(roleSelected);
            CheckedCommand.RegisterAsyncAction(o =>
                            {
                                var authModule = o as AuthModule;
                                if (authModule == null)
                                    return;
                                var authRoleFunction = new AuthRoleFunction();
                                authRoleFunction.FunctionKey = authModule.Key;
                                authRoleFunction.AuthRole = AuthRoles;

                                _roleFunctions.MakePersistent(authRoleFunction);
                                _roleFunctions.Commit();
                            });
            UncheckedCommand = new ReactiveAsyncCommand(roleSelected);
            UncheckedCommand.RegisterAsyncAction(o =>
                            {
                                var authModule = o as AuthModule;
                                if (authModule == null)
                                    return;
                                var authRoleFunction = _roleFunctions.GetRoleFunction(authModule.Key, AuthRoles);
                                _roleFunctions.MakeTransient(authRoleFunction);
                                _roleFunctions.Commit();

                            });

            AddCommand = new ReactiveAsyncCommand();
            AddCommand.Subscribe(o =>
                                     {
                                         var authRoles = DataBindingFactory.Create<AuthRoles>();

                                         var dialogViewModel = IoC.Get<IShowDialogViewModel<AuthRoles>>();
                                         dialogViewModel.ShowDialogContent = new AddRoles();
                                         dialogViewModel.Entity = authRoles;
                                         CanConfirm = dialogViewModel.CanConfirm;
                                         NotifyEntityChanged(authRoles);
                                         var showDialog = _windowManager.ShowDialog(dialogViewModel);
                                         if (showDialog == true)
                                         {
                                             AuthRolesCollection.Add(dialogViewModel.Entity);

                                             _repository.SaveOrUpdate(dialogViewModel.Entity);
                                             _repository.Commit();
                                         }
                                     });

            EditCommand = new ReactiveAsyncCommand(baseauthrole);
            EditCommand.Subscribe(o =>
                        {
                            var dialogViewModel = IoC.Get<IShowDialogViewModel<AuthRoles>>();
                            dialogViewModel.ShowDialogContent = new AddRoles();
                            dialogViewModel.Entity = BaseAuthRole;
                            CanConfirm = dialogViewModel.CanConfirm;
                            NotifyEntityChanged(BaseAuthRole);
                            var showDialog = _windowManager.ShowDialog(dialogViewModel);
                            if (showDialog == true)
                            {
                                _repository.SaveOrUpdate(dialogViewModel.Entity);
                                _repository.Commit();
                            }
                            else
                            {
                                _repository.Evict(dialogViewModel.Entity);
                                LoadAuthRoleCommand.Execute(this);
                            }
                        });

            RemoveCommand = new ReactiveAsyncCommand(baseauthrole);
            RemoveCommand.Subscribe(o =>
                                        {
                                            AuthRolesCollection.Remove(BaseAuthRole);

                                            _repository.SaveOrUpdate(BaseAuthRole);
                                            _repository.Commit();
                                        });

            SelectedUserCommand = new ReactiveAsyncCommand();
            var selectedUser = SelectedUserCommand.RegisterAsyncFunction(o =>
                                        {
                                            var user = o as Users;
                                            return user;
                                        });
            _user = new ObservableAsPropertyHelper<Users>(selectedUser, _ => { });
            var canSelectedUser = selectedUser.Select(x => x != null).StartWith(false);
            AddUserCommand = new ReactiveAsyncCommand();
            AddUserCommand.Subscribe(o =>
                    {
                        var users = DataBindingFactory.Create<Users>();
                        var dialogViewModel = IoC.Get<IShowDialogViewModel<Users>>();
                        dialogViewModel.ShowDialogContent = new AddUser();
                        dialogViewModel.Entity = users;
                        dialogViewModel.ViewModel = this;
                        CanConfirm = dialogViewModel.CanConfirm;
                        NotifyEntityChanged(users);
                        var showDialog = _windowManager.ShowDialog(dialogViewModel);
                        if (showDialog == true)
                        {
                            users.AgentId = users.AgentId;
                            users.Name = users.Name;
                            users.Password = users.Password;
                            users.Role = SelectedAuthRole;

                            UsersCollection.Add(users);

                            _userRepository.SaveOrUpdate(users);
                            _userRepository.Commit();
                        }
                    });

            EditUserCommand = new ReactiveAsyncCommand(canSelectedUser);
            EditUserCommand.Subscribe(o =>
                    {

                        var user = _userRepository.GetUserByAgentId(Users.AgentId);
                        Users.AgentId = user.AgentId;
                        Users.Name = user.Name;
                        SelectedAuthRole = user.Role;
                        var dialogViewModel = IoC.Get<IShowDialogViewModel<Users>>();
                        dialogViewModel.ShowDialogContent = new AddUser();
                        dialogViewModel.Entity = user;
                        dialogViewModel.ViewModel = this;
                        CanConfirm = dialogViewModel.CanConfirm;
                        NotifyEntityChanged(user);
                        var showDialog = _windowManager.ShowDialog(dialogViewModel);
                        if (showDialog == true)
                        {
                            user.Role = SelectedAuthRole;
                            _userRepository.SaveOrUpdate(user);
                            _userRepository.Commit();
                        }
                        else
                        {
                            _userRepository.Evict(user);
                            LoadUserCommand.Execute(this);
                        }
                    });

            RemoveUserCommand = new ReactiveAsyncCommand(canSelectedUser);
            RemoveUserCommand.Subscribe(o =>
                        {
                            if (Users == null)
                                return;

                            UsersCollection.Remove(Users);

                            _userRepository.MakeTransient(Users);
                            _userRepository.Commit();

                        });
        }


        protected override void OnInitialize()
        {

            _allFunction = FunctionModule.GetAllFunction();
            AuthModules = new ObservableCollection<AuthModule>();
            foreach (var i in _allFunction)
            {
                AuthModules.Add(new AuthModule(i.Key, i.Value, false));
            }

          

        }
        public ReactiveCollection<Users> UsersCollection
        {
            get { return _loadUserCollection.Value; }
        }

        public ReactiveCollection<AuthRoles> AuthRolesCollection
        {
            get { return _loadAuthRoleCollection.Value; }
        }
        public AuthRoles AuthRoles
        {
            get
            {
                return _authRoles.Value;
            }
        }

        public Users Users { get { return _user.Value; } }
        public AuthRoles BaseAuthRole { get { return _baseAuthRole.Value; } }
        private AuthRoles _selectedAuthRole;
        public AuthRoles SelectedAuthRole
        {
            get { return _selectedAuthRole; }
            set { _selectedAuthRole = value; NotifyOfPropertyChange(() => SelectedAuthRole); }
        }

        private bool _isRoleSelected;
        public bool IsRoleSelected
        {
            get { return _isRoleSelected; }
            set { _isRoleSelected = value; NotifyOfPropertyChange(() => IsRoleSelected); }
        }

        public Action<bool> CanConfirm;
        public bool CanSave(Users users)
        {
            return !string.IsNullOrEmpty(users.AgentId) && !string.IsNullOrEmpty(users.Name);

        }
        private void NotifyEntityChanged(Users users)
        {
            var entity = users as INotifyPropertyChanged;
            entity.SafeInvoke(x => x.PropertyChanged += (sender, args) =>
            {
                CanConfirm.Invoke(CanSave(users));
            });
        }
        public bool CanSave(AuthRoles roles)
        {
            return !string.IsNullOrEmpty(roles.RoleName);
        }
        private void NotifyEntityChanged(AuthRoles roles)
        {
            var entity = roles as INotifyPropertyChanged;
            entity.SafeInvoke(x => x.PropertyChanged += (sender, args) =>
            {
                CanConfirm.Invoke(CanSave(roles));
            });
        }
    }

    public class AuthModule : INotifyPropertyChanged
    {
        public AuthModule(string key, string name, bool isSelected)
        {
            Key = key;
            ModuleName = name;
            IsSelected = isSelected;
        }

        public string Key { get; set; }
        private string _moduleName;
        public string ModuleName
        {
            get { return _moduleName; }
            set { _moduleName = value; OnNotifyChanged("ModuleName"); }
        }

        private bool _isSelected;
        public bool IsSelected
        {
            get { return _isSelected; }
            set
            {
                _isSelected = value;
                OnNotifyChanged("IsSelected");
            }
        }

        private void OnNotifyChanged(string name)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(name));
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

    }

}
